home *** CD-ROM | disk | FTP | other *** search
/ Directorty Opus 5 - Magellan 2 / Opus 5 - Magellan 2.iso / Extras / hotlist_source / source / hotlist.module.c < prev    next >
C/C++ Source or Header  |  1996-10-18  |  77KB  |  2,344 lines

  1. /*######################################################################################
  2. ## hotlist.module by Leo 'Nudel' Davidson for Gods'Gift Utilities.                      ##
  3. ## A plug-in module for Directory Opus 5.5 to provide definable file/path hotlists.      ##
  4. ##                                                                                      ##
  5. ##                                                                                      ##
  6. ## Until July 1998 you should be able to contact me via any of the following:          ##
  7. ##                                                                                      ##
  8. ## email: leo.davidson@keble.oxford.ac.uk                                              ##
  9. ##   www: http://users.ox.ac.uk/~kebl0364                                              ##
  10. ##   IRC: Nudel in #Amiga on Effnet or Undernet (very rarely).                          ##
  11. ##                                                                                      ##
  12. ## Comments, suggestions, questions, offers, and chats all welcome.                      ##
  13. ##                                                                                      ##
  14. ##                                                                                      ##
  15. ## Tabsize: 4 -- 88 Columns (sorry) -- Amiga-specific -- Compile with SAS/C.          ##
  16. ##                                                                                      ##
  17. ## Credit is due to Nick Christie, Jonathan Potter, and Greg Perry for their advice,  ##
  18. ## examples, and general help beyond the call of duty. Thanks guys!                      ##
  19. ##************************************************************************************##
  20. ## There appears to be a bug in DOpus 5.5: Each call to AsyncRequestTags() looses      ##
  21. ## 32 bytes of memory. This also happens in the example module which comes with the      ##
  22. ## OpusSDK and has been reported to GPSoftware.                                          ##
  23. ########################################################################################
  24. ## This program never allocates more than about 30k at a time. Since all error          ##
  25. ## messages are output by requesters (which take quite a bit of mem to display),      ##
  26. ## memory allocation failures result in a silent abort. Perhaps it would be better      ##
  27. ## to at least call DisplayBeep() -- maybe in the future.                              ##
  28. ########################################################################################
  29. ## In order to stop one hotlist from modifying and saving config data underneath      ##
  30. ## another a semaphore is used which blocks access to ANY config file while other      ##
  31. ## hotlists are reading/writting. Ideally, and perhaps in the future, this locking      ##
  32. ## would be limited to hotlists using the same config file, although at times this      ##
  33. ## is actually an advantage. e.g. when adding a hotlist config file to another (it      ##
  34. ## will be checked to see if it is a config file which requires openning it).          ##
  35. ## Given that it is a rare, minor anoyance, is useful at times, and would be quite a  ##
  36. ## pain to reliably change, I am inclinded to keep it how it is.                      ##
  37. ######################################################################################*/
  38.  
  39. #include "hotlist.module.h"
  40.  
  41. /**************************************************************************************/
  42.  
  43. #define PROGNAME "hotlist.module"
  44. #define PROGVERS "1.2" // When changing this, don't forget to update the values in
  45.                        // the makefile to keep things consistent.
  46. #define PROGDATE __AMIGADATE__
  47. static char version_str[] = "\0$VER: " PROGNAME " " PROGVERS " " PROGDATE "\0";
  48. // Above line should be double null-terminated.
  49.  
  50. /*= Definition of the module =========================================================*/
  51. ModuleInfo module_info =
  52. {
  53.         1,                                    // Version
  54.         "hotlist.module",                    // Module name
  55.         "hotlist.catalog",                    // Catalog name
  56.         0,                                    // Flags
  57.         1,                                    // Number of functions
  58.         {0,"Hotlist",MSG_HOTLIST_DESC,\
  59.          FUNCF_SINGLE_SOURCE|FUNCF_WANT_SOURCE,\
  60.          CMD_TEMPLATE}    // First (only) function.
  61. };
  62.  
  63. /*= Trapped Commands =================================================================*/
  64.  
  65. const char *trapCmds[] =
  66. {
  67.     "Delete","MakeDir","ScanDir","Duplicate",
  68.     "Hotlist","PrintDir","DiskInfo","Copy","CopyAs","Move","MoveAs","Rename","Parent",
  69.     "Root","Comment","Protect","Read","HexRead","Show","Play","Assign","GetSizes",
  70.     "DateStamp"
  71. };
  72. #define TRAPPEDNUM (sizeof(trapCmds)/sizeof(const char *))
  73.  
  74. /**************************************************************************************/
  75.  
  76. /*= L_Module_Entry() =================================================================-.
  77. || Main entry point to the module. The L_ is to identify this as a library              ||
  78. || function (specified by the "libprefix" option in the makefile)                      ||
  79. `-====================================================================================*/
  80. int __asm __saveds L_Module_Entry(
  81.     register __a0 char *args,                // User-supplied arguments
  82.     register __a1 struct Screen *screen,    // Screen to open on
  83.     register __a2 IPCData *ipc,                // Our IPC pointer
  84.     register __a3 IPCData *main_ipc,        // Main Opus IPC pointer
  85.     register __d0 ULONG mod_id,                // ID of module function
  86.     register __d1 EXT_FUNC(func_callback))    // Opus callback function
  87. {
  88.     Hotlist_Data *data;                // Pseudo-global variables.
  89.     FuncArgs *fa;
  90.     ResNode *combufrn;
  91.     struct command_packet cp;
  92.     BYTE notsig;
  93.  
  94.     if (data = AllocVec(sizeof(Hotlist_Data),MEMF_CLEAR))
  95.     {
  96.         data->ipc = ipc;
  97.         data->func_callback = func_callback;
  98.         data->lister[0] = '\0';
  99.         (data->parent)[0] = '\0';
  100.         data->listerhandle = NULL;
  101.         data->conflist = NULL;
  102.  
  103.         if (data->rnd.poolhead = NewMemHandle(PUDDLESIZE,THRESHSIZE,MEMF_CLEAR))
  104.         {
  105.             data->rnd.data = data;
  106.  
  107.             // A separate pool is used for the linked list of hotlist-entries
  108.             // so that they can all be freed easily with one function call.
  109.             if (data->hentspool = NewMemHandle(HEPUDDLESIZE,HETHRESHSIZE,MEMF_CLEAR))
  110.             {
  111.                 if (DOpusBase->lib_Version < MIN_OPUS_VERSION)
  112.                     informUser(data,dgs(MSG_VERSREQ_FMT),FALSE,NULL,MIN_OPUS_VERSION);
  113.                 else if ((*((struct Library **)4))->lib_Version < MIN_EXEC_VERSION)
  114.                     informUser(data,dgs(MSG_EXECVERS_FMT),FALSE,NULL,MIN_EXEC_VERSION);
  115.                 else if (addMsgPort(data))
  116.                 {
  117.                     if ( (-1) == (notsig = AllocSignal(-1)) )
  118.                     {
  119.                         informUser(data,dgs(MSG_NOSIGS),FALSE,NULL);
  120.                     }
  121.                     else
  122.                     {
  123.                         fa = parseArgs(data,args);    // Parse command-line arguments.
  124.  
  125.                         // Setup the notify structure but don't turn it on.
  126.                         (data->notsigmask) = (1L << notsig);
  127.                         (data->notify.nr_stuff.nr_Signal.nr_SignalNum) = notsig;
  128.                         (data->notify.nr_stuff.nr_Signal.nr_Task) = FindTask(NULL);
  129.                         (data->notify.nr_Flags) = NRF_SEND_SIGNAL;
  130.                         (data->notify.nr_Name) = (data->config);
  131.                         (data->notifyon) = FALSE;
  132.  
  133.                         readConfigFile(data,FALSE);    // Parse config file.
  134.  
  135.                         if (getListerHandle(data) && \
  136.                             (combufrn = allocNewResNode(&data->rnd,COMMBUFFSIZE)) )
  137.                         {
  138.                             basicListerInit(data,combufrn->rn_Mem,&cp);
  139.  
  140.                             // Now setup of lister is complete and we're waiting
  141.                             // for events from the user.
  142.  
  143.                             mainEventLoop(data,combufrn->rn_Mem,&cp);
  144.  
  145.                             // We are no longer the active handler for the lister,
  146.                             // time to exit.
  147.  
  148.                             deleteResNode(&data->rnd,combufrn);
  149.                         }
  150.                         notifyOff(data);
  151.                         freeArgs(fa);            // Free FuncArgs structure, if any.
  152.                         FreeSignal(notsig);
  153.                     }
  154.                     remMsgPort(data);
  155.                 }
  156.                 FreeMemHandle(data->hentspool);
  157.                 data->hentspool = NULL;
  158.             }
  159.             // Make sure all ResourceNodes and contents freed in case of an abort.
  160.             deleteAllResNodes(&data->rnd);    // Safe to call even if no ResNodes.
  161.             // Free our memory pool.
  162.             FreeMemHandle(data->rnd.poolhead);
  163.             data->rnd.poolhead = NULL;
  164.         }
  165.         // Free out pseudo-global variables.
  166.         FreeVec(data);
  167.     }
  168.     // Currently, functions should always return 1.
  169.     return(1);
  170. }
  171.  
  172. /*= InformUser() =====================================================================-.
  173. || Send the user a requester with printf-style formatted text.                          ||
  174. || Requester is centered on the Opus screen and has just an "OK" gadget.              ||
  175. || If window is TRUE it'll try to open over the lister window.                          ||
  176. ||------------------------------------------------------------------------------------||
  177. || Flags: IU_CANCEL - Adds a "Cancel" button as well as "OK". Returns boolean.          ||
  178. `-====================================================================================*/
  179. long informUser(Hotlist_Data *data,char *format,BOOL window,ULONG flags,...)
  180. {
  181.     long iu_return = 0;
  182.     struct Window *win;
  183.     struct Screen *screen;
  184.     ResNode *rn1;
  185.     va_list  ap;
  186.  
  187.     if (rn1 = allocNewResNode(&data->rnd,INFORMUSERBUFFERSIZE))
  188.     {
  189.         // Build requester text
  190.         va_start(ap,flags);
  191.         vsprintf(rn1->rn_Mem,format,ap);
  192.         va_end(ap);
  193.  
  194.         if (window)
  195.         {
  196.             win = getListerWindow(data);
  197.             screen = NULL;
  198.         }
  199.         else
  200.         {
  201.             screen = getDOpusScreen(data);
  202.             win = NULL;
  203.         }
  204.  
  205.         // Display requester over window.
  206.         iu_return = AsyncRequestTags(data->ipc, REQTYPE_SIMPLE, 0, 0, 0,
  207.             TAGIF(win,AR_Window),    win,
  208.             TAGIF((!win) && screen,AR_Screen),    screen,
  209.             AR_Message,                rn1->rn_Mem,
  210.             AR_Title,                dgs(MSG_TITLE),
  211.             AR_Button,                dgs(MSG_OK_GAD),
  212.             TAGIF(flags & IU_CANCEL,AR_Button), dgs(MSG_CANCEL_GAD),
  213.             TAG_END);
  214.  
  215.         deleteResNode(&data->rnd,rn1);
  216.     }
  217.     return(iu_return);
  218. }
  219.  
  220. /*= GetString() ======================================================================-.
  221. || Send the user a string requester with printf-style formatted text.                  ||
  222. || Centred on the Opus screen unless win is TRUE.                                      ||
  223. || strbuff is the buffer to put the string into, bufflen its size.                      ||
  224. || Returns the number of the button they pressed (1 for "OK", 0 for "Cancel").          ||
  225. ||------------------------------------------------------------------------------------||
  226. || Flags: As for dopus5.library/AsyncRequest()                                          ||
  227. `-====================================================================================*/
  228. long getString(Hotlist_Data *data,char *format,BOOL window,ULONG flags,\
  229.                 char *strbuff,long bufflen,...)
  230. {
  231.     long gs_return = 0;
  232.     struct Window *win;
  233.     struct Screen *screen;
  234.     ResNode *rn1;
  235.     va_list  ap;
  236.  
  237.     if (rn1 = allocNewResNode(&data->rnd,INFORMUSERBUFFERSIZE))
  238.     {
  239.         // Build requester text
  240.         va_start(ap,bufflen);
  241.         vsprintf(rn1->rn_Mem,format,ap);
  242.         va_end(ap);
  243.  
  244.         if (window)
  245.         {
  246.             win = getListerWindow(data);
  247.             screen = NULL;
  248.         }
  249.         else
  250.         {
  251.             screen = getDOpusScreen(data);
  252.             win = NULL;
  253.         }
  254.  
  255.         // Display requester over window.
  256.         gs_return = AsyncRequestTags(data->ipc, REQTYPE_SIMPLE, 0, 0, 0,
  257.             TAGIF(win,AR_Window),    win,
  258.             TAGIF((!win) && screen,AR_Screen),    screen,
  259.             AR_Message,                rn1->rn_Mem,
  260.             AR_Title,                dgs(MSG_TITLE),
  261.             AR_Button,                dgs(MSG_OK_GAD),
  262.             AR_Button,                dgs(MSG_CANCEL_GAD),
  263.             AR_Buffer,                strbuff,
  264.             AR_BufLen,                bufflen,
  265.             AR_Flags,                flags,
  266.             TAG_END);
  267.  
  268.         deleteResNode(&data->rnd,rn1);
  269.     }
  270.     return(gs_return);
  271. }
  272.  
  273. /*= GetPathString() ==================================================================-.
  274. || Send the user a string requester with printf-style formatted text.                  ||
  275. || This requester will also have a "Browse" button which replaces the string          ||
  276. || requester with a file requester (transparent to caller -- it will be as if they      ||
  277. || typed the selected path into the string requester, or canceled it if they cancel). ||
  278. || Centred on the Opus screen unless win is TRUE.                                      ||
  279. || strbuff is the buffer to put the string into, bufflen its size.                      ||
  280. || Returns the number of the button they pressed (1 for "OK", 0 for "Cancel").          ||
  281. ||------------------------------------------------------------------------------------||
  282. || Flags: As for dopus5.library/AsyncRequest()                                          ||
  283. `-====================================================================================*/
  284. long getPathString(Hotlist_Data *data,char *format,BOOL window,ULONG flags,\
  285.                 char *strbuff,long bufflen,...)
  286. {
  287.     long gs_return = 0;
  288.     struct Window *win;
  289.     struct Screen *screen;
  290.     ResNode *rn1;
  291.     va_list  ap;
  292.     APTR req;
  293.     char *fpath;
  294.  
  295.     if (rn1 = allocNewResNode(&data->rnd,INFORMUSERBUFFERSIZE))
  296.     {
  297.         // Build requester text
  298.         va_start(ap,bufflen);
  299.         vsprintf(rn1->rn_Mem,format,ap);
  300.         va_end(ap);
  301.  
  302.         if (window)
  303.         {
  304.             win = getListerWindow(data);
  305.             screen = NULL;
  306.         }
  307.         else
  308.         {
  309.             screen = getDOpusScreen(data);
  310.             win = NULL;
  311.         }
  312.  
  313.         // Display requester over window.
  314.         gs_return = AsyncRequestTags(data->ipc, REQTYPE_SIMPLE, 0, 0, 0,
  315.             TAGIF(win,AR_Window),    win,
  316.             TAGIF((!win) && screen,AR_Screen),    screen,
  317.             AR_Message,                rn1->rn_Mem,
  318.             AR_Title,                dgs(MSG_TITLE),
  319.             AR_Button,                dgs(MSG_OK_GAD),
  320.             AR_Button,                dgs(MSG_BROWSE_GAD),
  321.             AR_Button,                dgs(MSG_CANCEL_GAD),
  322.             AR_Buffer,                strbuff,
  323.             AR_BufLen,                bufflen,
  324.             AR_Flags,                flags,
  325.             TAG_END);
  326.  
  327.         // If they selected "browse":
  328.         if (gs_return == 2)
  329.         {
  330.             gs_return = 0;
  331.  
  332.             if (req = AllocAslRequestTags(ASL_FileRequest,
  333.                         TAGIF(win,ASLFR_Window),    win,
  334.                         TAGIF((!win) && screen,ASLFR_Screen),    screen,
  335.                         ASLFR_InitialDrawer,        "SYS:",
  336.                         TAG_END))
  337.             {
  338.                 if (gs_return = AslRequestTags(req,TAG_END))
  339.                 {
  340.                     fpath = stpcpy(strbuff,((struct FileRequester *)req)->fr_Drawer);
  341.  
  342.                     // If there was a path string and it didn't end in "/" or ":",
  343.                     // insert a "/" before the filename.
  344.  
  345.                     if ( (((struct FileRequester *)req)->fr_Drawer[0]) && \
  346.                          (fpath[-1] != ':') && (fpath[-1] != '/') )
  347.                     {
  348.                         fpath = stpcpy(fpath,"/");
  349.                     }
  350.                     stpcpy(fpath,((struct FileRequester *)req)->fr_File);
  351.                 }
  352.                 FreeAslRequest(req);
  353.             }
  354.         }
  355.  
  356.         deleteResNode(&data->rnd,rn1);
  357.     }
  358.     return(gs_return);
  359. }
  360.  
  361. /*= getListerHandle() ================================================================-.
  362. || Fills in data->lister & data->listerhandle with handle of the lister we're to use. ||
  363. || If there is no source lister, or the NEW argument was given, a new lister is          ||
  364. || transparently created.                                                              ||
  365. || data->parent will be set to the path of the old lister, or to the empty string if  ||
  366. || a new lister was created or the path was unobtainable.                              ||
  367. ||------------------------------------------------------------------------------------||
  368. || If data->lister and data->listerhandle are already set (by the command-line parse) ||
  369. || they will be left, but data->parent will still be filled in.                          ||
  370. ||------------------------------------------------------------------------------------||
  371. || data->lister and data->parent *MUST* be set to the empty string before calling      ||
  372. || the command-line parser and then this routine.                                      ||
  373. ||------------------------------------------------------------------------------------||
  374. || Returns boolean success.                                                              ||
  375. `-====================================================================================*/
  376. BOOL getListerHandle(Hotlist_Data *data)
  377. {
  378.     BOOL glh_return = FALSE;
  379.     ResNode *combufrn;
  380.     struct command_packet cp;
  381.  
  382.     // Allocate a command buffer.
  383.     if (combufrn = allocNewResNode(&data->rnd,COMMBUFFSIZE))
  384.     {
  385.         struct path_node *pn;
  386.  
  387.         // If they didn't give the "NEW" switch, first attempt to get the
  388.         // source lister, unless we already have one (from command-line).
  389.         if ( (!(data->new)) && ((data->lister)[0] == '\0') )
  390.         {
  391.             // Killing two birds with one stone, get the old path into data->parent
  392.             // and get a pointer to the structure with the wanted lister handle, too.
  393.             // If the lister handle is NULL, we'll have to get a new one (this will
  394.             // if they press run us from the toolbar of a devicelist lister).
  395.             if ((pn = (struct path_node *)data->func_callback(EXTCMD_GET_SOURCE,\
  396.                                             IPCDATA(data->ipc),data->parent)) && \
  397.                 (pn->lister))
  398.             {
  399.                 glh_return = TRUE;        // We got one! -- Convert long to ASCII.
  400.                 stcl_d(data->lister,(long)(pn->lister));
  401.             }
  402.         }
  403.         // If the lister handle was given on the command-line, get the path from it.
  404.         else if (!(data->new))
  405.         {
  406.             sprintf(combufrn->rn_Mem,"lister query %s path",data->lister);
  407.             cp.flags = COMMANDF_RESULT;
  408.             cp.command = combufrn->rn_Mem;
  409.             cp.result = NULL;
  410.             if ((data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),&cp)) && \
  411.                 (cp.result) && (cp.result[0]))
  412.             {
  413.                 strcpy(data->parent,cp.result);
  414.             }
  415.  
  416.             if (cp.result)
  417.             {
  418.                 FreeVec(cp.result);
  419.                 cp.result = NULL;
  420.             }
  421.  
  422.             glh_return = TRUE;                    // We got one!
  423.         }
  424.  
  425. // If any source listers are locked now, unlock them. (Even if we're going to
  426. // use one of the source listers as it'll be made busy again shortly).
  427.  
  428.         data->func_callback(EXTCMD_UNLOCK_SOURCE,IPCDATA(data->ipc),NULL);
  429.  
  430.  
  431.         // If there still isn't a lister handle, either "NEW" was given or we
  432.         // couldn't get a source lister, so open a new one.
  433.         if ((data->lister)[0] == '\0')
  434.         {
  435.             // Just incase it was wrongly set TRUE in the get-source part.
  436.             glh_return = FALSE;
  437.  
  438.             // "lister new" command string, with or without snapshot geometry.
  439.             if ( ((data->snap.x) == (-1)) && ((data->snap.y) == (-1)) && \
  440.                  ((data->snap.w) == (-1)) && ((data->snap.h) == (-1)) )
  441.             {
  442.                 cp.command = "lister new";
  443.             }
  444.             else
  445.             {
  446.                 sprintf(combufrn->rn_Mem,"lister new %d/%d/%d/%d",
  447.                     data->snap.x,data->snap.y,data->snap.w,data->snap.h);
  448.                 cp.command = combufrn->rn_Mem;
  449.             }
  450.             cp.flags = COMMANDF_RESULT;            // We do want a result.
  451.             // Send the command.
  452.             if ( (data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),&cp)) && \
  453.                  ((cp.rc) == 0) && (cp.result) && (*(cp.result)) )
  454.             {
  455.                 strcpy(data->lister,cp.result);        // Store the handle string.
  456.                 FreeVec(cp.result);                    // Free the result string's mem.
  457.                 glh_return = TRUE;                    // We got one!
  458.             }
  459.         }
  460.  
  461.         deleteResNode(&data->rnd,combufrn);
  462.     }
  463.  
  464.     if (glh_return)
  465.     {
  466.         // If we got a lister handle in the end, store an APTR version of it, too.
  467.         (data->listerhandle) = (APTR)(atol(data->lister));
  468.     }
  469.  
  470.     return(glh_return);
  471. }
  472.  
  473. /*= sendExtCmd_nr() ==================================================================-.
  474. || Function to make calling Opus ARexx commands slightly cleaner. This version for      ||
  475. || when you do not want a result string.                                              ||
  476. `-====================================================================================*/
  477. void sendExtCmd_nr(Hotlist_Data *data,char *cmdstring,struct command_packet *cpp)
  478. {
  479.     // We do NOT want a result, but dopus5.library seems prone to
  480.     // memory leaks when one isn't requested for certain commands,
  481.     // so we'll ask for one and free it immediately afterwards.
  482.     cpp->flags = COMMANDF_RESULT;
  483.     cpp->command = cmdstring;
  484.     data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp);
  485.     FreeVec(cpp->result);
  486.     cpp->result = NULL;
  487. }
  488.  
  489. /*= addEntries() =====================================================================-.
  490. || Add the entries in the linked list of HotEntry structures to the lister.              ||
  491. || Does not refresh the lister.                                                          ||
  492. `-====================================================================================*/
  493. void addEntries(Hotlist_Data *data,char *combuf,struct command_packet *cpp)
  494. {
  495.     HotEntry *hep;
  496.  
  497.     if (data->hentbase)
  498.     {
  499.         for (hep = data->hentbase; hep; hep = hep->next)
  500.         {
  501.             // We do NOT want a result, but dopus5.library seems prone to
  502.             // memory leaks when one isn't requested for certain commands,
  503.             // so we'll ask for one and free it immediately afterwards.
  504.             // (Haven't used sendExtCmd_nr for some extra speed here.)
  505.             cpp->flags = COMMANDF_RESULT;
  506.             cpp->command = combuf;
  507.             sprintf(combuf,"lister add %s \"%s\" 0 %d 0 rwed (%s)",
  508.                 data->lister,hep->name,hep->type,hep->path);
  509.             data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp);
  510.             FreeVec(cpp->result);
  511.             cpp->result = NULL;
  512.         }
  513.     }
  514.     else
  515.     {
  516.         // Otherwise add a default entry saying "Empty: drop here" type thing.
  517.         sprintf(combuf,"lister add %s \"%s\" 0 %d 0 rwed (%s)",
  518.                 data->lister,dgs(MSG_DEFAULTNAME),-2,dgs(MSG_DEFAULTPATH));
  519.         sendExtCmd_nr(data,combuf,cpp);
  520.     }
  521. }
  522.  
  523. /*= mainEventLoop() ==================================================================-.
  524. || Once the lister is setup, this loops around until an inactive event, dealing with  ||
  525. || the events the user throws at it.                                                  ||
  526. `-====================================================================================*/
  527. void mainEventLoop(Hotlist_Data *data,char *combuf,struct command_packet *cpp)
  528. {
  529.     struct StandardPacket *pkt;
  530.     ULONG msgsigmask;
  531.     ULONG signals;
  532.     BOOL stoploop = FALSE;
  533.     char *a0;
  534.     char *a1;
  535.     char *a2;
  536.     char *a3;
  537.     char *a4;
  538.     char *a5;
  539.     char *a6;
  540.  
  541.     msgsigmask = (1L << (data->msgport->mp_SigBit));
  542.  
  543.     while(TRUE)
  544.     {
  545.         signals = Wait((data->notsigmask) | (msgsigmask));
  546.  
  547.         // If we got a notification request, re-read the config.
  548.         if (signals & (data->notsigmask))
  549.             rereadConfig(data,combuf,cpp);
  550.  
  551.         // If there may be one or more messages on our port, process them.
  552.         if (signals & msgsigmask)
  553.         {
  554.             while ((!stoploop) && (pkt=(struct StandardPacket *)GetMsg(data->msgport)))
  555.             {
  556.                 // Note that the arg?'s start from 0 to sync with Opus ARexx Guide.
  557.                 a0 = (char *)pkt->sp_Pkt.dp_Arg1;
  558.                 a1 = (char *)pkt->sp_Pkt.dp_Arg2;
  559.                 a2 = (char *)pkt->sp_Pkt.dp_Arg3;
  560.                 a3 = (char *)pkt->sp_Pkt.dp_Arg4;
  561.                 a4 = (char *)pkt->sp_Pkt.dp_Arg5;
  562.                 a5 = (char *)pkt->sp_Pkt.dp_Arg6;
  563.                 a6 = (char *)pkt->sp_Pkt.dp_Arg7;
  564. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  565.                 if (strcmp(a0,"inactive") == 0)
  566.                 {
  567.                     stoploop = event_inactive(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  568.                 }
  569.                 else if (strcmp(a0,"doubleclick") == 0)
  570.                 {
  571.                     stoploop = event_doubleclick(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  572.                 }
  573.                 else if (strcmp(a0,"dropfrom") == 0)
  574.                 {
  575.                     stoploop = event_dropfrom(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  576.                 }
  577.                 else if (strcmp(a0,"drop") == 0)
  578.                 {
  579.                     stoploop = event_drop(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  580.                 }
  581.                 else if (strcmp(a0,"MakeDir") == 0)
  582.                 {
  583.                     stoploop = event_makedir(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  584.                 }
  585.                 else if (strcmp(a0,"Delete") == 0)
  586.                 {
  587.                     stoploop = event_delete(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  588.                 }
  589.                 else if (strcmp(a0,"path") == 0)
  590.                 {
  591.                     stoploop = event_path(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  592.                 }
  593.                 else if ((strcmp(a0,"ScanDir") == 0) || (strcmp(a0,"Hotlist") == 0))
  594.                 {
  595.                     stoploop = event_scandir_hotlist(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  596.                 }
  597.                 else if (strcmp(a0,"snapshot") == 0)
  598.                 {
  599.                     stoploop = event_snapshot(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  600.                 }
  601.                 else if (strcmp(a0,"unsnapshot") == 0)
  602.                 {
  603.                     stoploop = event_unsnapshot(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  604.                 }
  605.                 else if (strcmp(a0,"Rename") == 0)
  606.                 {
  607.                     stoploop = event_rename(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  608.                 }
  609.                 else if (strcmp(a0,"parent") == 0)
  610.                 {
  611.                     stoploop = event_parent(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  612.                 }
  613.                 else if (strcmp(a0,"Duplicate") == 0)
  614.                 {
  615.                     stoploop = event_duplicate(data,combuf,cpp,a1,a2,a3,a4,a5,a6);
  616.                 }
  617.                 else
  618.                 {
  619.                     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  620.                     sendExtCmd_nr(data,combuf,cpp);
  621.                     informUser(data,dgs(MSG_UNSUPPORTED_FMT),TRUE,0,a0);
  622.                     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  623.                     sendExtCmd_nr(data,combuf,cpp);
  624.                 }
  625. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  626.                 ReplyMsg((struct Message *)pkt);
  627.             }
  628.             if (stoploop) break;
  629.         }
  630.     }
  631. }
  632.  
  633. /*= writeConfigFile() ================================================================-.
  634. || Attempts to write the current entry data to the config file. If anything fails an  ||
  635. || error requester will probably be shown to the user, but there is no return result; ||
  636. || the rest of the program should continue even if the config file couldn't be          ||
  637. || written.                                                                              ||
  638. ||------------------------------------------------------------------------------------||
  639. || Before the write happens, notification will be turned off (if on), and then back      ||
  640. || on again after the write.                                                              ||
  641. ||------------------------------------------------------------------------------------||
  642. || Before calling this you **MUST** get an exclusive lock on the config semaphore.      ||
  643. || After calling this you must release the lock, unless you still need it.              ||
  644. || You should use getWriteConfigSemaphore() for locking -- see that for more info.      ||
  645. `-====================================================================================*/
  646. void writeConfigFile(Hotlist_Data *data)
  647. {
  648.     BOOL ifffailed = FALSE;
  649.     HotEntry *hent;
  650.     APTR iffhandle;
  651.     int confver;
  652.  
  653.     confver = CONFIGVER;
  654.  
  655.     notifyOff(data);
  656.  
  657. // Even if there are no hotlist entries, we should still write the config file
  658. // because it also contains snapshot information (and maybe more in the future).
  659.  
  660.     if (iffhandle = IFFOpen(data->config,IFF_WRITE|IFF_SAFE,ID_DOHL))
  661.     {
  662.         if ( (!(IFFWriteChunk(iffhandle,&confver,ID_VERS,sizeof(int)))) || \
  663.              (!(IFFWriteChunk(iffhandle,&data->snap,ID_SNAP,sizeof(data->snap)))) || \
  664.              (!(IFFWriteChunk(iffhandle,&data->time,ID_TIME,sizeof(data->time)))) )
  665.             ifffailed = TRUE;
  666.  
  667.         for (hent = data->hentbase; (!ifffailed) && hent; hent = hent->next)
  668.         {
  669.             // Only save the entry if it hasn't been marked for deletion.
  670.             if (!(hent->deleted))
  671.             {
  672.                 // The NAME field must always be first as it is used when reading
  673.                 // the config file to designate the start of each new entry.
  674.                 if ((!IFFWriteChunk(iffhandle,hent->name,ID_NAME,strlen(hent->name)+1))\
  675.                  || (!IFFWriteChunk(iffhandle,hent->path,ID_PATH,strlen(hent->path)+1))\
  676.                  || (!IFFWriteChunk(iffhandle,&hent->type,ID_TYPE,sizeof(hent->type))) )
  677.                 {
  678.                     ifffailed = TRUE;
  679.                 }
  680.             }
  681.         }
  682.  
  683.         if (ifffailed)
  684.         {
  685.             informUser(data,dgs(MSG_CONFIGWRITE_ERROR),TRUE,NULL);
  686.             IFFFailure(iffhandle);
  687.         }
  688.  
  689.         IFFClose(iffhandle);
  690.     }
  691.     else
  692.     {
  693.         informUser(data,dgs(MSG_CONFIGWRITE_ERROR),TRUE,NULL);
  694.     }
  695.  
  696.     notifyOn(data);
  697. }
  698.  
  699. /*= readConfigFile() =================================================================-.
  700. || Attempts to replace the existing config (if any) in memory with that stored in the ||
  701. || config file.                                                                          ||
  702. || If the config file could not be read, hentbase will be NULL and the snapshot          ||
  703. || position will be disabled (all -1). This will happen when the user hasn't yet      ||
  704. || made a hotlist, for example, as well as when there is a genuine error.              ||
  705. ||------------------------------------------------------------------------------------||
  706. || Before the read happens, notification will be turned off (if on), and then back      ||
  707. || on again after the read.                                                              ||
  708. ||------------------------------------------------------------------------------------||
  709. || Will wait until it can get a shared lock on the config semaphore.                  ||
  710. || Set alreadylocked to TRUE if we already have an exclusive lock on the semaphore      ||
  711. || (e.g. if this routine is called once we get exclusive access to the config file      ||
  712. || but the file needs re-reading as it has changed). Should never call this routine      ||
  713. || with a shared lock on the semaphore.                                                  ||
  714. `-====================================================================================*/
  715. void readConfigFile(Hotlist_Data *data,BOOL alreadylocked)
  716. {
  717.     BOOL iffsucc;
  718.     HotEntry *chent;
  719.     ULONG chunkid;
  720.     APTR iffhandle;
  721.     struct ClockData newtime;
  722.     ULONG tsecs;
  723.     ULONG tmics;
  724.  
  725.     iffsucc = FALSE;
  726.  
  727.     CurrentTime(&tsecs,&tmics);
  728.     Amiga2Date(tsecs,&newtime);
  729.  
  730.     // Default geometry for new lister. -1 for all means "not snapshotted".
  731.     data->snap.x = data->snap.y = data->snap.w = data->snap.h = -1;
  732.  
  733.     data->time.sec   = newtime.sec;
  734.     data->time.min   = newtime.min;
  735.     data->time.hour  = newtime.hour;
  736.     data->time.mday  = newtime.mday;
  737.     data->time.month = newtime.month;
  738.     data->time.year  = newtime.year;
  739.     data->time.wday  = newtime.wday;
  740.  
  741.     notifyOff(data);
  742.  
  743.     // Get a shared lock on our semaphore.
  744.     if (!(alreadylocked))
  745.         getConfigSemaphore(data,GCS_SHARED);
  746.  
  747.     if (iffhandle = IFFOpen(data->config,IFF_READ,ID_DOHL))
  748.     {
  749.         iffsucc = TRUE;
  750.         chent = NULL;
  751.         while ((iffsucc) && (chunkid = IFFNextChunk(iffhandle,0)))
  752.         {
  753.             // The NAME chunk designates a new hotlist entry.
  754.             if ((chunkid == ID_NAME) && (chent = newHotEntry(data)))
  755.                 iffsucc = IFFReadChunkBytes(iffhandle,chent->name,sizeof(chent->name));
  756.             else if ((chent) && (chunkid == ID_PATH))
  757.                 iffsucc = IFFReadChunkBytes(iffhandle,chent->path,sizeof(chent->path));
  758.             else if ((chent) && (chunkid == ID_TYPE))
  759.                 iffsucc = IFFReadChunkBytes(iffhandle,&chent->type,sizeof(chent->type));
  760.             else if (chunkid == ID_SNAP)
  761.                 iffsucc = IFFReadChunkBytes(iffhandle,&data->snap,sizeof(data->snap));
  762.             else if (chunkid == ID_TIME)
  763.                 iffsucc = IFFReadChunkBytes(iffhandle,&data->time,sizeof(data->time));
  764.         }
  765.         IFFClose(iffhandle);
  766.     }
  767.  
  768.     if (!iffsucc)
  769.     {
  770.         if (IoErr() != ERROR_OBJECT_NOT_FOUND)
  771.             informUser(data,dgs(MSG_CONFIGREAD_ERROR),TRUE,NULL);
  772.  
  773.         // Default geometry for new lister. -1 for all means "not snapshotted".
  774.         data->snap.x = data->snap.y = data->snap.w = data->snap.h = -1;
  775.  
  776.         data->hentbase = NULL;                // Make sure hentbase is NULL.
  777.         ClearMemHandle(data->hentspool);    // Free all hotentry memory (pool remains).
  778.     }
  779.  
  780.     // The semaphore must be released NOW as splent() below may want to write
  781.     // to the config file (attempting an EXCLUSIVE lock), which would cause a
  782.     // deadlock otherwise.
  783.     if (!(alreadylocked))
  784.         ReleaseSemaphore(configsemaphore);
  785.  
  786.     notifyOn(data);
  787.  
  788.     if ((newtime.mday != data->time.mday) || (newtime.month != data->time.month))
  789.     {
  790.         if ((newtime.mday == 25) && (newtime.month == 12))
  791.             splent(data,alreadylocked,&newtime,"Nfssz Disjtunbt!");
  792.         else if ((newtime.mday == 1) && (newtime.month == 1))
  793.             splent(data,alreadylocked,&newtime,"Ibqqz Ofx Zfbs!");
  794.         else if ((newtime.mday == 20) && ((newtime.month)%3 == 0))
  795.             splent(data,alreadylocked,&newtime,
  796.                                  "Ifz, opu tp gbtu. J'n tjdl pg cfjoh vtfe,\n" \
  797.                                  "zpv bmxbzt kvtu pqfo nf vq, dipptf b\n" \
  798.                                  "ejsfdupsz, boe uifo J'n hpof. Zpv ofwfs\n" \
  799.                                  "ubml up nf. Ebnnju, zpv usfbu nf mjlf ejsu.\n" \
  800.                                  "Jg zpv epo'u tubsu qbzjoh nf npsf buufoujpo,\n" \
  801.                                  "J njhiu kvtu hp po tusjlf. Zfbi, tff ipx\n" \
  802.                                  "uibu nblft zpv gffm. Qsphsbnt ibwf sjhiut\n" \
  803.                                  "zpv lopx, tp, jo uif gvuvsf, cf ojdf.");
  804.         else if ((newtime.mday == 29) && (newtime.month == 2))
  805.             splent(data,alreadylocked,&newtime,
  806.                                  "29ui pg Gfcsvbsz.\n" \
  807.                                  "Zpv epo'u tff nboz pg uiptf...");
  808.         else if ((newtime.mday == 7) && (newtime.month == 1))
  809.             splent(data,alreadylocked,&newtime,
  810.                              "Upebz jt uif cjsuiebz pg nz dsfbups, Mfp Ebwjetpo.\n" \
  811.                              "Zpv sfbmmz tipvme tfoe ijn b hjgu, if't b usvfmz\n" \
  812.                              "xpoefsgvm qfstpo boe J pxf fwfszuijoh up ijn. :-)\n" \
  813.                              "Bu mfbtu tfoe bo fnbjm (cfgpsf Kvmz 1998) up ijn:\n\n" \
  814.                              "mfp.ebwjetpo@lfcmf.pygpse.bd.vl");
  815.         else if ((newtime.mday == 9) && (newtime.month == 7))
  816.             splent(data,alreadylocked,&newtime,
  817.                                  "J ibwf up ufmm zpv tpnfuijoh.\n\n" \
  818.                                  "J lopx J'n pomz b ipumjtu, cvu usvf mpwf\n" \
  819.                                  "lopxt op cpvoet. Zft -- J mpwf zpv. J ibwf\n" \
  820.                                  "bmxbzt mpwfe zpv tjodf uif ebz zpv gjstu\n" \
  821.                                  "jotfsufe zpvs fousjft joup nf. Opx J dboopu\n" \
  822.                                  "lffq uiftf gffmjoht up nztfmg boznpsf.");
  823.         else if ((newtime.mday == 15) && (newtime.month == 4))
  824.             splent(data,alreadylocked,&newtime,
  825.                                  "J bn Ipumjtuipmjp.\n" \
  826.                                  "J offe UQ gps nz cvohipmf.\n\n" \
  827.                                  "Bsf zpv uisfbufojoh nf?\n" \
  828.                                  "ZPV XJMM HJWF NF UQ, CVOHIPMJP!\n\n" \
  829.                                  "...xifsf J dpnf gspn uifz ibwf op cvohipmft...");
  830.         else if ((newtime.mday == 15) && (newtime.month == 8))
  831.             splent(data,alreadylocked,&newtime,"CPP!\n\n(Eje J tdbsf zpv?)");
  832.         else if ((newtime.mday == 12) && (newtime.month == 10))
  833.             splent(data,alreadylocked,&newtime,
  834.                                  "Boe xibu jg J epo'u xbou up cf b ipumjtu upebz?");
  835.     }
  836. }
  837.  
  838. /*= splent() =========================================================================-.
  839. || A harmless surprise every so often. :-) (Don't spoil the fun and tell everyone!)      ||
  840. `-====================================================================================*/
  841. void splent(Hotlist_Data *data,BOOL alreadylocked,struct ClockData *newtime,char *msg)
  842. {
  843.     char c;
  844.     char *p;
  845.     ResNode *memrn;
  846.  
  847.     if (memrn = allocNewResNode(&data->rnd,4096))
  848.     {
  849.         if (!(alreadylocked))
  850.             getWriteConfigSemaphore(data);
  851.  
  852.         data->time.sec = newtime->sec;
  853.         data->time.min = newtime->min;
  854.         data->time.hour = newtime->hour;
  855.         data->time.mday = newtime->mday;
  856.         data->time.month = newtime->month;
  857.         data->time.year = newtime->year;
  858.         data->time.wday = newtime->wday;
  859.  
  860.         p = memrn->rn_Mem;
  861.  
  862.         // Not exactly PGP, I know. :-) Does the job, though.
  863.         while(c = *msg++)
  864.         {
  865.             if (isalpha(c))
  866.             {
  867.                 if (c == 'A')
  868.                     c = 'Z';
  869.                 else if (c == 'a')
  870.                     c = 'z';
  871.                 else
  872.                     c--;
  873.             }
  874.             *p++ = c;
  875.         }
  876.         *p = '\0';
  877.  
  878.         informUser(data,memrn->rn_Mem,FALSE,NULL);
  879.  
  880.         writeConfigFile(data);        // Don't do this one again ('till next year).
  881.         if (!(alreadylocked))
  882.             ReleaseSemaphore(configsemaphore);
  883.  
  884.         deleteResNode(&data->rnd,memrn);
  885.     }
  886. }
  887.  
  888. /*= rereadConfig() ===================================================================-.
  889. || Clears all entries from the lister, frees all HotEntries, reloads the config file  ||
  890. || and adds the entries to the lister.                                                  ||
  891. ||------------------------------------------------------------------------------------||
  892. || This routien MUST do a full refresh on the lister, even if it doesn't require it      ||
  893. || itself in the future as at least two other routines (at this time) depend on it.      ||
  894. `-====================================================================================*/
  895. void rereadConfig(Hotlist_Data *data,char *combuf,struct command_packet *cpp)
  896. {
  897.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  898.     sendExtCmd_nr(data,combuf,cpp);
  899.  
  900.     // Note that this is "clear", not "empty" like in the initial setup.
  901.     sprintf(combuf,"lister clear %s",data->lister);
  902.     sendExtCmd_nr(data,combuf,cpp);
  903.  
  904.     data->hentbase = NULL;                // Make sure hentbase is NULL.
  905.     ClearMemHandle(data->hentspool);    // Free all hotentry memory (pool remains).
  906.  
  907.     readConfigFile(data,FALSE);
  908.     addEntries(data,combuf,cpp);
  909.  
  910.     sprintf(combuf,"lister refresh %s full",data->lister);
  911.     sendExtCmd_nr(data,combuf,cpp);
  912.  
  913.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  914.     sendExtCmd_nr(data,combuf,cpp);
  915. }
  916.  
  917. /*= newHotEntry() ====================================================================-.
  918. || Allocate a new HotEntry structure, add it to the linked list, and give it some      ||
  919. || default values. Returns NULL on failure.                                              ||
  920. `-====================================================================================*/
  921. HotEntry *newHotEntry(Hotlist_Data *data)
  922. {
  923.     HotEntry *nhe;
  924.  
  925.     if (nhe = AllocMemH(data->hentspool,sizeof(HotEntry)))
  926.     {
  927.         // Add to front of linked list.
  928.         (nhe->next) = (data->hentbase);
  929.         (data->hentbase) = nhe;
  930.  
  931.         // Give it some defaults, just in case the config file is old or invalid
  932.         // and doesn't define them. There's little point in localizing these as
  933.         // they should never really be seen.
  934.         strcpy(nhe->name,"--errname--");
  935.         strcpy(nhe->path,"--errpath--");
  936.         (nhe->type) = (-1);
  937.         (nhe->deleted) = FALSE;
  938.     }
  939.  
  940.     return(nhe);
  941. }
  942.  
  943. /*= remHotEntry() ====================================================================-.
  944. || Remove and deallocate the most recently added HotEntry structure.                  ||
  945. `-====================================================================================*/
  946. void remHotEntry(Hotlist_Data *data)
  947. {
  948.     HotEntry *ohe;
  949.  
  950.     if (data->hentbase)
  951.     {
  952.         ohe = (data->hentbase);
  953.         (data->hentbase) = (ohe->next);
  954.  
  955.         FreeMemH((APTR) ohe);
  956.     }
  957. }
  958.  
  959. /*= wordcpy() ========================================================================-.
  960. || Copy the string from source to dest until the first quote or NULL in source.          ||
  961. || source will be advanced to point to the character after the second quote if there  ||
  962. || is one, or the NULL terminator otherwise.                                          ||
  963. `-====================================================================================*/
  964. void wordcpy(char *dest,char **source)
  965. {
  966.     while((**source) && (**source != '"'))
  967.         *(dest++) = *((*source)++);
  968.     *dest = '\0';
  969.  
  970.     if (**source == '"')
  971.     {
  972.         (*source)++;
  973.         if (**source == ' ')
  974.         {
  975.             (*source)++;
  976.             if (**source == '"')
  977.                 (*source)++;
  978.         }
  979.     }
  980. }
  981.  
  982. /*= wordCount() ======================================================================-.
  983. || Counts the number of quote characters in the string, divides by two, and returns      ||
  984. || the number.                                                                          ||
  985. `-====================================================================================*/
  986. int wordcount(char *wordstring)
  987. {
  988.     int wcnt = 0;
  989.  
  990.     if (wordstring)
  991.     {
  992.         while(*wordstring)
  993.         {
  994.             if (*(wordstring++) == '"')
  995.                 wcnt++;
  996.         }
  997.  
  998.         wcnt /= 2;
  999.     }
  1000.  
  1001.     return(wcnt);
  1002. }
  1003.  
  1004. /*= findHotEntry() ===================================================================-.
  1005. || Searches along the HotEntries linked list until it finds one which has the same      ||
  1006. || name as that given. Returns NULL if there is no match.                              ||
  1007. || Will not match entries which have been marked as "deleted".                          ||
  1008. || The match is *not* case-sensitive.                                                  ||
  1009. `-====================================================================================*/
  1010. HotEntry *findHotEntry(Hotlist_Data *data,char *searchname)
  1011. {
  1012.     HotEntry *he;
  1013.  
  1014.     for (he = (data->hentbase); he; he = he->next)
  1015.     {
  1016.         if ((!(he->deleted)) && (stricmp(he->name,searchname) == 0))
  1017.             break;
  1018.     }
  1019.  
  1020.     return(he);
  1021. }
  1022.  
  1023. /*= findOldEntry() ===================================================================-.
  1024. || Searches along the HotEntries linked list until it finds one which has the same      ||
  1025. || name as that given. Returns TRUE if there's a match which isn't the most recently  ||
  1026. || added entry, FALSE otherwise.                                                      ||
  1027. || Will not match entries which have been marked as "deleted".                          ||
  1028. || The match is *not* case-sensitive.                                                  ||
  1029. `-====================================================================================*/
  1030. BOOL findOldEntry(Hotlist_Data *data,char *searchname)
  1031. {
  1032.     HotEntry *he = NULL;
  1033.  
  1034.     if ((data->hentbase) && (data->hentbase->next))
  1035.     {
  1036.         for (he = (data->hentbase->next); he; he = he->next)
  1037.         {
  1038.             if ((!(he->deleted)) && (stricmp(he->name,searchname) == 0))
  1039.                 break;
  1040.         }
  1041.     }
  1042.  
  1043.     return((BOOL)he);
  1044. }
  1045.  
  1046. /*= setHotType() =====================================================================-.
  1047. || Based on the path of the given HotEntry, sets the type of the entry.                  ||
  1048. || If it can't work it out it'll default to being a directory.                          ||
  1049. || Those entries recognised as files may be openned and checked to see if they're      ||
  1050. || hotlist.module config files (which have their own type).                              ||
  1051. `-====================================================================================*/
  1052. void setHotType(Hotlist_Data *data,HotEntry *nhe)
  1053. {
  1054.     ResNode *bufrn;
  1055.     ResNode *rn;
  1056.     char *buf;
  1057.     BPTR lock;
  1058.     APTR iffhandle;
  1059.  
  1060.     // Default to the directory type if anything fails.
  1061.     (nhe->type) = HOTTYPE_DIR;
  1062.  
  1063.     if (bufrn = allocNewResNode(&data->rnd,PATHBUFFSIZE))
  1064.     {
  1065.         buf = (char *)bufrn->rn_Mem;
  1066.         if (rn = createResNode(&data->rnd))
  1067.         {
  1068.             (rn->rn_Name) = (nhe->path);
  1069.             if (lock = lockFileResNode(&data->rnd,rn,ACCESS_READ))
  1070.             {
  1071.                 if ( (NameFromLock(lock,buf,PATHBUFFSIZE)) && \
  1072.                      ( buf[strlen(buf)-1] == ':') )
  1073.                 {
  1074.                     (nhe->type) = HOTTYPE_DEV;
  1075.                 }
  1076.                 else if ( (examineResNode(&data->rnd,rn)) && \
  1077.                           ((rn->rn_FIB->fib_DirEntryType) < 0) )
  1078.                 {
  1079.                     // It's a file -- see if it matches the hotlist.module
  1080.                     // IFF config type. If so it gets the special HOTTYPE_HOT type.
  1081.                     if (iffhandle = IFFOpen(rn->rn_Name,IFF_READ,ID_DOHL))
  1082.                     {
  1083.                         IFFClose(iffhandle);
  1084.                         (nhe->type) = HOTTYPE_HOT;
  1085.                     }
  1086.                     else
  1087.                         (nhe->type) = HOTTYPE_FILE;
  1088.                 }
  1089.                 else
  1090.                 {
  1091.                     (nhe->type) = HOTTYPE_DIR;
  1092.                 }
  1093.             }
  1094.             else
  1095.             {
  1096.                 // If we cannot lock the file, assume it doesn't exist and
  1097.                 // call it a new hotlist.
  1098.                 (nhe->type) = HOTTYPE_HOT;
  1099.             }
  1100.             deleteResNode(&data->rnd,rn);
  1101.         }
  1102.         deleteResNode(&data->rnd,bufrn);
  1103.     }
  1104. }
  1105.  
  1106. /*= getListerWindow() ================================================================-.
  1107. || Gets the window of a lister from just the lister handle (in ASCII).                  ||
  1108. || Returns the handle, or NULL. This should not be stored for later use as the window ||
  1109. || may be different next time (for example, Opus has reopenned on another screen).      ||
  1110. || This is a bit of a hack, but Jonathan Potter has said it should be okay.              ||
  1111. `-====================================================================================*/
  1112. struct Window *getListerWindow(Hotlist_Data *data)
  1113. {
  1114.     struct path_node pn;
  1115.     struct Window *win = NULL;
  1116.  
  1117.     if (data->listerhandle)
  1118.     {
  1119.         // Setup some semi-sensible defaults (and hope they'll do!)
  1120.         pn.buffer[0] = '\0';
  1121.         pn.path = pn.buffer;
  1122.         pn.flags = NULL;
  1123.  
  1124.         pn.lister = (data->listerhandle);
  1125.  
  1126.         win = (struct Window *)\
  1127.                 data->func_callback(EXTCMD_GET_WINDOW,IPCDATA(data->ipc),(APTR)&pn);
  1128.     }
  1129.     return(win);
  1130. }
  1131.  
  1132. /*= getDOpusScreen() =================================================================-.
  1133. || Attempts to get the Opus screen, returns it or NULL.                                  ||
  1134. || The return should not be stored for later use as the screen may be different next  ||
  1135. || time if the user has changed it.                                                      ||
  1136. `-====================================================================================*/
  1137. struct Screen *getDOpusScreen(Hotlist_Data *data)
  1138. {
  1139.     struct Screen *screen = NULL;
  1140.     struct DOpusScreenData *dsd;
  1141.  
  1142.     if (dsd = (struct DOpusScreenData *)\
  1143.                 data->func_callback(EXTCMD_GET_SCREENDATA,IPCDATA(data->ipc),NULL))
  1144.     {
  1145.         screen = dsd->screen;
  1146.  
  1147.         data->func_callback(EXTCMD_FREE_SCREENDATA,IPCDATA(data->ipc),(APTR)dsd);
  1148.     }
  1149.     return(screen);
  1150. }
  1151.  
  1152. /*= listerRead() =====================================================================-.
  1153. || Restores the original lister settings and then reads the given path in.              ||
  1154. ||------------------------------------------------------------------------------------||
  1155. || **IMPORTANT** This routine removes our handler from the lister. After calling we      ||
  1156. || ************* should shutdown.                                                      ||
  1157. `-====================================================================================*/
  1158. void listerRead(Hotlist_Data *data,char *combuf,struct command_packet *cpp,char *path)
  1159. {
  1160.     sprintf(combuf,"lister clear %s",data->lister);
  1161.     sendExtCmd_nr(data,combuf,cpp);
  1162.  
  1163.     sprintf(combuf,"lister set %s handler",data->lister);
  1164.     sendExtCmd_nr(data,combuf,cpp);
  1165.  
  1166.     sprintf(combuf,"lister set %s lock state %s format %s",data->lister,"off","off");
  1167.     sendExtCmd_nr(data,combuf,cpp);
  1168.  
  1169.     sprintf(combuf,"lister set %s namelength %d",data->lister,31);
  1170.     sendExtCmd_nr(data,combuf,cpp);
  1171.  
  1172.     sprintf(combuf,"lister empty %s",data->lister);
  1173.     sendExtCmd_nr(data,combuf,cpp);
  1174.  
  1175.     sprintf(combuf,"lister set %s field %s",data->lister,"on");
  1176.     sendExtCmd_nr(data,combuf,cpp);
  1177.  
  1178.     sprintf(combuf,"lister refresh %s full",data->lister);
  1179.     sendExtCmd_nr(data,combuf,cpp);
  1180.  
  1181.     sprintf(combuf,"lister read %s \"%s\"",data->lister,path);
  1182.     sendExtCmd_nr(data,combuf,cpp);
  1183. }
  1184.  
  1185. /*= parseArgs() ======================================================================-.
  1186. || Parses command-line arguments and sets the "NEW" switch and "CONFIG" file name.      ||
  1187. || If there is no command-line, or some kind of error/problem occurs during parsing,  ||
  1188. || defaults will be used.                                                              ||
  1189. `-====================================================================================*/
  1190. FuncArgs *parseArgs(Hotlist_Data *data,char *args)
  1191. {
  1192.     FuncArgs *fa;
  1193.  
  1194.     // Defaults.
  1195.     (data->new) = FALSE;
  1196.     (data->config) = DEFAULT_CONFIG;
  1197.  
  1198.     if (fa = ParseArgs(CMD_TEMPLATE,args))
  1199.     {
  1200.         (data->new) = (BOOL)((fa->FA_Arguments)[ARG_NEW]);
  1201.  
  1202.         if ((fa->FA_Arguments)[ARG_CONFIG])
  1203.         {
  1204.             (data->config) = (char *)((fa->FA_Arguments)[ARG_CONFIG]);
  1205.         }
  1206.  
  1207.         if ( ((fa->FA_Arguments)[ARG_LISTER]) && 
  1208.              (strlen((char *)((fa->FA_Arguments)[ARG_LISTER])) < 24) )
  1209.         {
  1210.             strcpy(data->lister,(char *)((fa->FA_Arguments)[ARG_LISTER]));
  1211.         }
  1212.     }
  1213.  
  1214.     (data->originalconfig) = (data->config);
  1215.  
  1216.     return(fa);
  1217. }
  1218.  
  1219. /*= freeArgs() =======================================================================-.
  1220. || Frees the structure returned by parseArgs(), if one returned at all.                  ||
  1221. || All pointers into the structure will be invalid after this call.                      ||
  1222. `-====================================================================================*/
  1223. void freeArgs(FuncArgs *fa)
  1224. {
  1225.     if (fa)
  1226.         DisposeArgs(fa);
  1227. }
  1228.  
  1229. /*= endcpy() =========================================================================-.
  1230. || Copies the source string to the dest string (of length destlen). If the dest is      ||
  1231. || too small it'll chop off enough characters from the source to make the END of it      ||
  1232. || fit in.                                                                              ||
  1233. `-====================================================================================*/
  1234. void endcpy(char *dest,char *source,long destlen)
  1235. {
  1236.     long diff;
  1237.  
  1238.     diff = strlen(source) - (destlen-1);
  1239.  
  1240.     if (diff > 0)
  1241.         source += diff;
  1242.  
  1243.     strcpy(dest,source);
  1244. }
  1245.  
  1246. /*= leofilepart() ====================================================================-.
  1247. || Calls dos.library/FilePart() on the string and returns the result if non-empty,      ||
  1248. || otherwise it returns the original string. Only (intended) difference is that if      ||
  1249. || you pass a device string like "DH0:" it won't return the empty string, it'll          ||
  1250. || return "DH0:".                                                                      ||
  1251. `-====================================================================================*/
  1252. char *leofilepart(char *path)
  1253. {
  1254.     char *lfp;
  1255.  
  1256.     lfp = FilePart(path);
  1257.  
  1258.     if (lfp[0] == '\0')
  1259.         lfp = path;
  1260.  
  1261.     return(lfp);
  1262. }
  1263.  
  1264. /*= notHandled() =====================================================================-.
  1265. || Returns:                                                                              ||
  1266. || >0 if there is no handler attached to the lister handle given (ASCII), or the      ||
  1267. ||    lister handle given is the empty string (e.g. drop not from a lister).          ||
  1268. ||  0 if there is a hotlist handler attached which is not our one.                      ||
  1269. || <0 if there is either a non-hotlist handler or our hotlist handler attached.          ||
  1270. ||                                                                                      ||
  1271. || When there is a non-hotlist handler attached it puts up an error requester saying  ||
  1272. || something along the lines of "You cannot drop entries between a Hotlist and          ||
  1273. || another custom-handler driven lister."                                              ||
  1274. ||                                                                                      ||
  1275. || In certain situations where a drop didn't go to/come from a lister at all, the      ||
  1276. || handle Opus returns is ours instead of empty (AFAIK this only happens when you      ||
  1277. || drop from our lister to the main Opus window). To bypass this situation, if the      ||
  1278. || lister handle string is the same as data->lister, <0 will still be returned,          ||
  1279. || (since we still want to ignore the event), but instead of the error message being  ||
  1280. || shown, DisplayBeep() will be called.                                                  ||
  1281. `-====================================================================================*/
  1282. LONG notHandled(Hotlist_Data *data,char *combuf,struct command_packet *cpp,char *lh)
  1283. {
  1284.     LONG nh_return = 1;
  1285.  
  1286.     if ((lh) && (lh[0]))
  1287.     {
  1288.         // If it's our lister, complain with a DisplayBeep and send back -1.
  1289.         if (strcmp(lh,data->lister) == 0)
  1290.         {
  1291.             nh_return = (-1);
  1292.             DisplayBeep(NULL);
  1293.         }
  1294.         else
  1295.         {
  1296.             sprintf(combuf,"lister query %s handler",lh);
  1297.             cpp->flags = COMMANDF_RESULT;
  1298.             cpp->command = combuf;
  1299.             cpp->result = NULL;
  1300.             if ((data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp)) && \
  1301.                 (cpp->result) && (cpp->result[0] != '\0'))
  1302.             {
  1303.                 if (strncmp(cpp->result,PORTNAME_PREFIX,strlen(PORTNAME_PREFIX))
  1304.                          == 0)
  1305.                 {
  1306.                     // If it's another hotlist handler, return 0.
  1307.                     nh_return = 0;
  1308.                 }
  1309.                 else
  1310.                 {
  1311.                     // If it's a handler but not a hotlist one, return -1.
  1312.                     nh_return = (-1);
  1313.                     informUser(data,dgs(MSG_NOCUSTDROP),TRUE,0);
  1314.                 }
  1315.             }
  1316.             // else, leave it as 1.
  1317.  
  1318.             if (cpp->result)
  1319.             {
  1320.                 FreeVec(cpp->result);
  1321.                 cpp->result = NULL;
  1322.             }
  1323.         }
  1324.     }
  1325.     return(nh_return);
  1326. }
  1327.  
  1328. /*= addMsgPort() =====================================================================-.
  1329. || Creates a message port with unique name and adds it to the port list.              ||
  1330. `-====================================================================================*/
  1331. BOOL addMsgPort(Hotlist_Data *data)
  1332. {
  1333.     BOOL amp_return = FALSE;
  1334.     int pnum;
  1335.  
  1336.     if (data->msgport = CreateMsgPort())
  1337.     {
  1338.         (data->msgport->mp_Node.ln_Pri) = 2;
  1339.         (data->msgport->mp_Node.ln_Name) = (data->mpname);
  1340.  
  1341.         pnum = 0;
  1342.         Forbid();
  1343.         do
  1344.         {
  1345.             sprintf(data->mpname,PORTNAME_FMT,pnum);
  1346.             pnum++;
  1347.         } while (FindPort(data->mpname));
  1348.         AddPort(data->msgport);
  1349.         Permit();
  1350.  
  1351.         amp_return = TRUE;
  1352.     }
  1353.     return(amp_return);
  1354. }
  1355.  
  1356. /*= remMsgPort() =====================================================================-.
  1357. || Removes message port from the port list, replies to all waiting messages and then  ||
  1358. || removes the port itself.                                                              ||
  1359. `-====================================================================================*/
  1360. void remMsgPort(Hotlist_Data *data)
  1361. {
  1362.     struct Message *tmpmsg;
  1363.  
  1364.     RemPort(data->msgport);
  1365.  
  1366.     while (tmpmsg = GetMsg(data->msgport))
  1367.         ReplyMsg(tmpmsg);
  1368.  
  1369.     DeleteMsgPort(data->msgport);
  1370.     data->msgport = NULL;
  1371. }
  1372.  
  1373. /*= basicListerInit() ================================================================-.
  1374. || Sends a bunch of ARexx commands to our lister to initialize it to how we want it      ||
  1375. || at the start of the program.                                                          ||
  1376. `-====================================================================================*/
  1377. void basicListerInit(Hotlist_Data *data,char *combuf,struct command_packet *cpp)
  1378. {
  1379.     // Wait for the lister to finish openning, if required.
  1380.     sprintf(combuf,"lister wait %s quick",data->lister);
  1381.     sendExtCmd_nr(data,combuf,cpp);
  1382.  
  1383.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1384.     sendExtCmd_nr(data,combuf,cpp);
  1385.  
  1386.     sprintf(combuf,"lister empty %s",data->lister);
  1387.     sendExtCmd_nr(data,combuf,cpp);
  1388.  
  1389.     sprintf(combuf,"lister set %s field %s",data->lister,"off");
  1390.     sendExtCmd_nr(data,combuf,cpp);
  1391.  
  1392.     sprintf(combuf,"lister set %s display name comment",data->lister);
  1393.     sendExtCmd_nr(data,combuf,cpp);
  1394.  
  1395.     sprintf(combuf,"lister set %s separate filesfirst",data->lister);
  1396.     sendExtCmd_nr(data,combuf,cpp);
  1397.  
  1398.     sprintf(combuf,"lister set %s namelength %d",data->lister,HOTENTNAMELEN);
  1399.     sendExtCmd_nr(data,combuf,cpp);
  1400.  
  1401.     sprintf(combuf,"lister set %s off",data->lister);
  1402.     sendExtCmd_nr(data,combuf,cpp);
  1403.  
  1404.     sprintf(combuf,"lister set %s path",data->lister);
  1405.     sendExtCmd_nr(data,combuf,cpp);
  1406.  
  1407.     sprintf(combuf,"lister set %s title %s",data->lister,dgs(MSG_TITLE));
  1408.     sendExtCmd_nr(data,combuf,cpp);
  1409.  
  1410.     sprintf(combuf,"lister set %s header (%s)",data->lister,data->config);
  1411.     sendExtCmd_nr(data,combuf,cpp);
  1412.  
  1413.     sprintf(combuf,"lister set %s label %s",data->lister,dgs(MSG_TITLE));
  1414.     sendExtCmd_nr(data,combuf,cpp);
  1415.  
  1416.     sprintf(combuf,"lister set %s mode name",data->lister);
  1417.     sendExtCmd_nr(data,combuf,cpp);
  1418.  
  1419.     sprintf(combuf,"lister set %s show #?",data->lister);
  1420.     sendExtCmd_nr(data,combuf,cpp);
  1421.  
  1422.     sprintf(combuf,"lister set %s hide",data->lister);
  1423.     sendExtCmd_nr(data,combuf,cpp);
  1424.  
  1425.     sprintf(combuf,"lister set %s lock state %s format %s",data->lister,"on","on");
  1426.     sendExtCmd_nr(data,combuf,cpp);
  1427.  
  1428.     // Refresh here makes format changes take effect and stops things looking
  1429.     // ugly if entries show up while being added, which they do sometimes.
  1430.     sprintf(combuf,"lister refresh %s full",data->lister);
  1431.     sendExtCmd_nr(data,combuf,cpp);
  1432.  
  1433.     // Add our handler.
  1434.     sprintf(combuf,"lister set %s handler %s quotes fullpath",
  1435.             data->lister,data->mpname);
  1436.     sendExtCmd_nr(data,combuf,cpp);
  1437.  
  1438.     // Trap all known commands. We still want to trap those commands which won't
  1439.     // be implimented so that we can ignore them when run, instead of them
  1440.     // attempting to run and failing on our lister.
  1441.     {
  1442.         int tnum;
  1443.         for (tnum = 0; tnum < TRAPPEDNUM; tnum++)
  1444.         {
  1445.             sprintf(combuf,"dopus addtrap %s %s",trapCmds[tnum],
  1446.                 data->mpname);
  1447.             sendExtCmd_nr(data,combuf,cpp);
  1448.         }
  1449.     }
  1450.  
  1451.     // Add all the entries.
  1452.     addEntries(data,combuf,cpp);
  1453.  
  1454.     sprintf(combuf,"lister refresh %s full",data->lister);
  1455.     sendExtCmd_nr(data,combuf,cpp);
  1456.  
  1457.     // Lister setup done, Unbusy
  1458.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1459.     sendExtCmd_nr(data,combuf,cpp);
  1460. }
  1461.  
  1462. /*= event_inactive() =================================================================-.
  1463. || In Opus 5.5 there appears to be a bug which sometimes sends bogus "inactive"          ||
  1464. || events to handlers. In an attempt to bypass this problem, when an "inactive"          ||
  1465. || event is received it will be ignored if our lister still has our handler attached. ||
  1466. `-====================================================================================*/
  1467. BOOL event_inactive(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1468.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1469. {
  1470.     BOOL stoploop = TRUE;
  1471.  
  1472.     sprintf(combuf,"lister query %s handler",data->lister);
  1473.     cpp->flags = COMMANDF_RESULT;
  1474.     cpp->command = combuf;
  1475.     cpp->result = NULL;
  1476.     if ((data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp)) && \
  1477.         (cpp->result) && (strcmp(cpp->result,data->mpname) == 0))
  1478.     {
  1479.         // If there is a handle attached to our (old) lister, and it is our
  1480.         // handler, assume the "inactive" message was bogus as we are clearly
  1481.         // still active.
  1482.         stoploop = FALSE;
  1483.     }
  1484.  
  1485.     if (cpp->result)
  1486.     {
  1487.         FreeVec(cpp->result);
  1488.         cpp->result = NULL;
  1489.     }
  1490.  
  1491.     return(stoploop);
  1492. }
  1493.  
  1494. /*= event_doubleclick() ==============================================================-.
  1495. `-====================================================================================*/
  1496. BOOL event_doubleclick(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1497.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1498. {
  1499.     BOOL stoploop = FALSE;
  1500.     ConfigListNode *clnp;
  1501.     ResNode *nrnp;
  1502.     HotEntry *he;
  1503.  
  1504.     if (he = findHotEntry(data,arg2))
  1505.     {
  1506.         if (he->type == HOTTYPE_FILE)
  1507.         {
  1508.             sprintf(combuf,"command doubleclick %s",he->path);
  1509.             sendExtCmd_nr(data,combuf,cpp);
  1510.         }
  1511.         else if (he->type == HOTTYPE_HOT)
  1512.         {
  1513.             if (strstr(arg6,"shift"))
  1514.             {
  1515.                 sprintf(combuf,"command hotlist NEW CONFIG \"%s\"",he->path);
  1516.                 sendExtCmd_nr(data,combuf,cpp);
  1517.             }
  1518.             else
  1519.             {
  1520.                 // Store old value.
  1521.                 clnp = (data->conflist);
  1522.  
  1523.                 if ( (nrnp) = allocNewResNode(&data->rnd,sizeof(ConfigListNode)) )
  1524.                 {
  1525.                     (data->conflist) = (nrnp->rn_Mem);
  1526.                     (data->conflist->rn_self) = (nrnp);
  1527.                     // Store pointer to the previous config filename.
  1528.                     (data->conflist->parent) = clnp;
  1529.  
  1530.                     if ((data->conflist->rn) = allocNewResNode(&data->rnd,
  1531.                                                                 strlen(he->path) + 1))
  1532.                     {
  1533.                         strcpy(data->conflist->rn->rn_Mem,he->path);
  1534.                         (data->config) = (data->conflist->rn->rn_Mem);
  1535.                         notifyOff(data);
  1536.                         (data->notify.nr_Name) = (data->config);
  1537.                         sprintf(combuf,"lister set %s header (%s)",data->lister,
  1538.                                                                     data->config);
  1539.                         sendExtCmd_nr(data,combuf,cpp);
  1540.                         rereadConfig(data,combuf,cpp);
  1541.                     }
  1542.                     else
  1543.                     {
  1544.                         DisplayBeep(NULL);
  1545.                         deleteResNode(&data->rnd,nrnp);
  1546.                         // Restore old value.
  1547.                         (data->conflist) = clnp;
  1548.                     }
  1549.                 }
  1550.                 else
  1551.                 {
  1552.                     (data->conflist) = clnp;
  1553.                     DisplayBeep(NULL);
  1554.                 }
  1555.             }
  1556.         }
  1557.         else if (strstr(arg6,"shift"))
  1558.         {
  1559.             sprintf(combuf,"lister new %s",he->path);
  1560.             sendExtCmd_nr(data,combuf,cpp);
  1561.         }
  1562.         else
  1563.         {
  1564.             stoploop = TRUE;
  1565.             listerRead(data,combuf,cpp,he->path);
  1566.         }
  1567.     }
  1568.  
  1569.     return(stoploop);
  1570. }
  1571.  
  1572. /*= event_dropfrom() =================================================================-.
  1573. `-====================================================================================*/
  1574. BOOL event_dropfrom(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1575.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1576. {
  1577.     HotEntry *he;
  1578.  
  1579.     if (0 < notHandled(data,combuf,cpp,arg3))
  1580.     {
  1581.         // Point past the initial quote.
  1582.         if (*arg2 == '"') arg2++;
  1583.         wordcpy(combuf,&arg2);    // Copy the name of the first entry.
  1584.  
  1585.         if (he = findHotEntry(data,combuf))
  1586.         {
  1587.             if (he->type == HOTTYPE_FILE)
  1588.             {
  1589.                 sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1590.                 sendExtCmd_nr(data,combuf,cpp);
  1591.                 informUser(data,dgs(MSG_NOFILEDROP),TRUE,NULL);
  1592.                 sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1593.                 sendExtCmd_nr(data,combuf,cpp);
  1594.             }
  1595.             else if (he->type == HOTTYPE_HOT)
  1596.             {
  1597.                 sprintf(combuf,"command hotlist CONFIG %s LISTER %s",he->path,arg3);
  1598.                 sendExtCmd_nr(data,combuf,cpp);
  1599.             }
  1600.             else
  1601.             {
  1602.                 sprintf(combuf,"lister read %s \"%s\"",arg3,he->path);
  1603.                 sendExtCmd_nr(data,combuf,cpp);
  1604.             }
  1605.         }
  1606.     }
  1607.  
  1608.     return(FALSE);
  1609. }
  1610.  
  1611. /*= event_drop() =====================================================================-.
  1612. `-====================================================================================*/
  1613. BOOL event_drop(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1614.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1615. {
  1616.     int  qnamelen;
  1617.     char *charp;
  1618.     BOOL gsr, fher;
  1619.     BOOL needsave = FALSE;
  1620.     LONG handtype;
  1621.     HotEntry *nhe;
  1622.  
  1623.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1624.     sendExtCmd_nr(data,combuf,cpp);
  1625.  
  1626.     if ( 0 <= (handtype = notHandled(data,combuf,cpp,arg3)) )
  1627.     {
  1628.         getWriteConfigSemaphore(data);
  1629.  
  1630.         // Point past the initial quote.
  1631.         if (*arg2 == '"') arg2++;
  1632.  
  1633.         while(*arg2)    // Do all words in the list (multi-drop).
  1634.         {
  1635.             if (*arg2 == '"')
  1636.                 arg2++;        // If it's an empty path, skip it.
  1637.             else
  1638.             {
  1639.                 // Allocate a new HotEntry structure.
  1640.                 if (nhe = newHotEntry(data))
  1641.                 {
  1642.                     if (handtype > 0)
  1643.                     {
  1644.                         // For entries not from another hotlist.
  1645.  
  1646.                         // Fill in the path by copying up to first quote. arg2 will
  1647.                         // then point to the first letter after the second quote, if
  1648.                         // there is one, or the null term.
  1649.                         wordcpy(nhe->path,&arg2);
  1650.                         endcpy(nhe->name,leofilepart(nhe->path),HOTENTNAMELEN);
  1651.  
  1652.                         do
  1653.                         {
  1654.                             gsr = getString(data,dgs(MSG_NAMEFORPATH_FMT),TRUE,0,
  1655.                                             nhe->name,HOTENTNAMELEN,nhe->path);
  1656.                             if ((gsr) && (fher = findOldEntry(data,nhe->name)))
  1657.                             {
  1658.                                 informUser(data,dgs(MSG_NAMEDUPLIC_FMT),TRUE,0,
  1659.                                             nhe->name);
  1660.                             }
  1661.                         } while((gsr) && (fher));
  1662.                     }
  1663.                     else
  1664.                     {
  1665.                         // For entries from another hotlist.
  1666.  
  1667.                         // Name is the filename.
  1668.                         wordcpy(nhe->name,&arg2);
  1669.  
  1670.                         // Get the path from the comment in the other hotlist.
  1671.                         sprintf(combuf,"lister query %s entry \"%s\"",arg3,nhe->name);
  1672.                         cpp->flags = COMMANDF_RESULT;
  1673.                         cpp->command = combuf;
  1674.                         cpp->result = NULL;
  1675.  
  1676.                         gsr = FALSE;
  1677.  
  1678.                         if ( (data->func_callback(EXTCMD_SEND_COMMAND,
  1679.                                                  IPCDATA(data->ipc),cpp)) && \
  1680.                              (cpp->result) && \
  1681.                              (strlen(cpp->result) > strlen(nhe->name)) )
  1682.                         {
  1683.                             charp = skipSpaces((cpp->result) + strlen(nhe->name),6);
  1684.                             qnamelen = strlen(charp);
  1685.                             if ((charp[0] == '(') && (charp[qnamelen-1] == ')'))
  1686.                             {
  1687.                                 strncpy(nhe->path,charp+1,qnamelen-2);
  1688.                                 (nhe->path)[qnamelen-2] = '\0';
  1689.                                 gsr = TRUE;
  1690.                             }
  1691.                             else
  1692.                             {
  1693.                                 informUser(data,dgs(MSG_PATHQUERY),TRUE,0);
  1694.                             }
  1695.                         }
  1696.  
  1697.                         if (cpp->result)
  1698.                         {
  1699.                             FreeVec(cpp->result);
  1700.                             cpp->result = NULL;
  1701.                         }
  1702.  
  1703.                         // Now make sure the name is unique for our hotlist.
  1704.                         // (We needed the ORIGINAL name above to query the entry)
  1705.  
  1706.                         while ((gsr) && (findOldEntry(data,nhe->name)))
  1707.                         {
  1708.                             informUser(data,dgs(MSG_NAMEDUPLIC_FMT),TRUE,0,
  1709.                                         nhe->name);
  1710.                             gsr = getString(data,dgs(MSG_NAMEFORPATH_FMT),TRUE,0,
  1711.                                             nhe->name,HOTENTNAMELEN,nhe->path);
  1712.                         }
  1713.                     }
  1714.  
  1715.                     if ((gsr) && (nhe->name[0]))
  1716.                     {
  1717.                         setHotType(data,nhe);    // Set the type of entry.
  1718.                         needsave = TRUE;        // Flag: at least one to save.
  1719.                     }
  1720.                     else
  1721.                     {
  1722.                         remHotEntry(data);    // Else abort this entry.
  1723.                         break;                // Don't do more entries either.
  1724.                     }
  1725.                 }
  1726.             }
  1727.         }
  1728.  
  1729.         if (needsave)
  1730.         {
  1731.             writeConfigFile(data);
  1732.             rereadConfig(data,combuf,cpp);
  1733.         }
  1734.  
  1735.         ReleaseSemaphore(configsemaphore);
  1736.     }
  1737.  
  1738.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1739.     sendExtCmd_nr(data,combuf,cpp);
  1740.  
  1741.     return(FALSE);
  1742. }
  1743.  
  1744. /*= event_makedir() ==================================================================-.
  1745. `-====================================================================================*/
  1746. BOOL event_makedir(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1747.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1748. {
  1749.     BOOL gsr, fher;
  1750.     HotEntry *nhe;
  1751.  
  1752.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1753.     sendExtCmd_nr(data,combuf,cpp);
  1754.  
  1755.     getWriteConfigSemaphore(data);
  1756.  
  1757.     // Allocate a new HotEntry structure.
  1758.     if (nhe = newHotEntry(data))
  1759.     {
  1760.         nhe->path[0] = '\0';
  1761.         if ( (getPathString(data,dgs(MSG_PATHFORNEW_NEW),TRUE,0,
  1762.                 nhe->path,HOTENTPATHLEN))  &&  (nhe->path[0]) )
  1763.         {
  1764.             endcpy(nhe->name,leofilepart(nhe->path),HOTENTNAMELEN);
  1765.  
  1766.             do
  1767.             {
  1768.                 gsr = getString(data,dgs(MSG_NAMEFORPATH_FMT),TRUE,0,nhe->name,
  1769.                                 HOTENTNAMELEN,nhe->path);
  1770.                 if ( (gsr) && (fher = findOldEntry(data,nhe->name)) )
  1771.                 {
  1772.                     informUser(data,dgs(MSG_NAMEDUPLIC_FMT),TRUE,0,nhe->name);
  1773.                 }
  1774.             } while( (gsr) && (fher) );
  1775.  
  1776.             if ( (gsr) && (nhe->name[0]) )
  1777.             {
  1778.                 setHotType(data,nhe);    // Set the type of entry.
  1779.                 writeConfigFile(data);
  1780.                 rereadConfig(data,combuf,cpp);
  1781.             }
  1782.             else
  1783.                 remHotEntry(data);    // Else abort adding this entry.
  1784.         }
  1785.         else
  1786.             remHotEntry(data);    // Else abort adding this entry.
  1787.     }
  1788.  
  1789.     ReleaseSemaphore(configsemaphore);
  1790.  
  1791.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1792.     sendExtCmd_nr(data,combuf,cpp);
  1793.  
  1794.     return(FALSE);
  1795. }
  1796.  
  1797. /*= event_delete() ===================================================================-.
  1798. `-====================================================================================*/
  1799. BOOL event_delete(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1800.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1801. {
  1802.     LONG infotxtid;
  1803.     int wcnt;
  1804.  
  1805.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1806.     sendExtCmd_nr(data,combuf,cpp);
  1807.  
  1808.     getWriteConfigSemaphore(data);
  1809.  
  1810.     if ((wcnt = wordcount(arg2)) == 1)
  1811.         infotxtid = MSG_DELETE_FMT_SINGLE;
  1812.     else
  1813.         infotxtid = MSG_DELETE_FMT_PLURAL;
  1814.  
  1815.     if (informUser(data,dgs(infotxtid),TRUE,IU_CANCEL,wcnt))
  1816.     {
  1817.         BOOL needsave = FALSE;
  1818.         HotEntry *delhe;
  1819.         ResNode *delnamern;
  1820.  
  1821.         if (delnamern = allocNewResNode(&data->rnd,HOTENTNAMELEN))
  1822.         {
  1823.             // Point past the initial quote.
  1824.             if (*arg2 == '"') arg2++;
  1825.  
  1826.             while(*arg2)    // Do all words in the list (multi-drop).
  1827.             {
  1828.                 // Fill in the name buffer by copying up to first quote. arg2 will then
  1829.                 // point to the first letter after the second quote, if there is one,
  1830.                 // or the null terminator.
  1831.                 wordcpy(delnamern->rn_Mem,&arg2);
  1832.  
  1833.                 if (delhe = findHotEntry(data,delnamern->rn_Mem))
  1834.                 {
  1835.                     (delhe->deleted) = TRUE;
  1836.                     needsave = TRUE;
  1837.                 }
  1838.             }
  1839.  
  1840.             if (needsave)
  1841.             {
  1842.                 writeConfigFile(data);
  1843.                 rereadConfig(data,combuf,cpp);
  1844.             }
  1845.  
  1846.             deleteResNode(&data->rnd,delnamern);
  1847.         }
  1848.     }
  1849.  
  1850.     ReleaseSemaphore(configsemaphore);
  1851.  
  1852.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1853.     sendExtCmd_nr(data,combuf,cpp);
  1854.  
  1855.     return(FALSE);
  1856. }
  1857.  
  1858. /*= event_path() =====================================================================-.
  1859. `-====================================================================================*/
  1860. BOOL event_path(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1861.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1862. {
  1863.     BOOL stoploop = FALSE;
  1864.  
  1865.     if (arg2[0])
  1866.     {
  1867.         stoploop = TRUE;
  1868.         listerRead(data,combuf,cpp,arg2);
  1869.     }
  1870.     else
  1871.     {
  1872.         sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1873.         sendExtCmd_nr(data,combuf,cpp);
  1874.         rereadConfig(data,combuf,cpp);
  1875.         sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1876.         sendExtCmd_nr(data,combuf,cpp);
  1877.     }
  1878.  
  1879.     return(stoploop);
  1880. }
  1881.  
  1882. /*= event_scandir_hotlist() ==========================================================-.
  1883. `-====================================================================================*/
  1884. BOOL event_scandir_hotlist(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1885.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1886. {
  1887.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1888.     sendExtCmd_nr(data,combuf,cpp);
  1889.  
  1890.     rereadConfig(data,combuf,cpp);
  1891.  
  1892.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1893.     sendExtCmd_nr(data,combuf,cpp);
  1894.  
  1895.     return(FALSE);
  1896. }
  1897.  
  1898. /*= event_snapshot() =================================================================-.
  1899. `-====================================================================================*/
  1900. BOOL event_snapshot(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1901.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1902. {
  1903.     int x,y,w,h;
  1904.  
  1905.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1906.     sendExtCmd_nr(data,combuf,cpp);
  1907.  
  1908.     getWriteConfigSemaphore(data);
  1909.  
  1910.     sprintf(combuf,"lister query %s position",data->lister);
  1911.  
  1912.     cpp->flags = COMMANDF_RESULT;
  1913.     cpp->command = combuf;
  1914.     data->func_callback(EXTCMD_SEND_COMMAND,IPCDATA(data->ipc),cpp);
  1915.  
  1916.     // Backup the old values in case of failure.
  1917.     x = data->snap.x;
  1918.     y = data->snap.y;
  1919.     w = data->snap.w;
  1920.     h = data->snap.h;
  1921.  
  1922.     if (4 == sscanf(cpp->result,"%d/%d/%d/%d",&(data->snap.x),\
  1923.                     &(data->snap.y),&(data->snap.w),&(data->snap.h)))
  1924.     {
  1925.         // Write new config (No point in re-reading it).
  1926.         writeConfigFile(data);
  1927.     }
  1928.     else
  1929.     {
  1930.         // Failure: Restore the old values.
  1931.         data->snap.x = x;
  1932.         data->snap.y = y;
  1933.         data->snap.w = w;
  1934.         data->snap.h = h;
  1935.     }
  1936.     
  1937.     FreeVec(cpp->result);
  1938.     cpp->result = NULL;
  1939.  
  1940.     ReleaseSemaphore(configsemaphore);
  1941.  
  1942.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1943.     sendExtCmd_nr(data,combuf,cpp);
  1944.  
  1945.     return(FALSE);
  1946. }
  1947.  
  1948. /*= event_unsnapshot() ===============================================================-.
  1949. `-====================================================================================*/
  1950. BOOL event_unsnapshot(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1951.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1952. {
  1953.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1954.     sendExtCmd_nr(data,combuf,cpp);
  1955.  
  1956.     getWriteConfigSemaphore(data);
  1957.  
  1958.     data->snap.x = data->snap.y = data->snap.w = data->snap.h = (-1);
  1959.     writeConfigFile(data);                            // No point re-reading the config.
  1960.  
  1961.     ReleaseSemaphore(configsemaphore);
  1962.  
  1963.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  1964.     sendExtCmd_nr(data,combuf,cpp);
  1965.  
  1966.     return(FALSE);
  1967. }
  1968.  
  1969. /*= event_rename() ===================================================================-.
  1970. `-====================================================================================*/
  1971. BOOL event_rename(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  1972.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  1973. {
  1974.     BOOL needsave = FALSE;
  1975.     BOOL gsr, fher;
  1976.     HotEntry *ohe;
  1977.     HotEntry *nhe;
  1978.     ResNode *orn;
  1979.  
  1980.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  1981.     sendExtCmd_nr(data,combuf,cpp);
  1982.  
  1983.     getWriteConfigSemaphore(data);
  1984.  
  1985.     if (orn = allocNewResNode(&data->rnd,HOTENTNAMELEN))
  1986.     {
  1987.         // Point past the initial quote.
  1988.         if (*arg2 == '"') arg2++;
  1989.  
  1990.         while(*arg2)    // Do all words in the list (multi-drop).
  1991.         {
  1992.             wordcpy(orn->rn_Mem,&arg2);
  1993.  
  1994.             if (ohe = findHotEntry(data,orn->rn_Mem))
  1995.             {
  1996.                 if (nhe = newHotEntry(data))
  1997.                 {
  1998.                     strcpy(nhe->path,ohe->path);
  1999.                     strcpy(nhe->name,ohe->name);
  2000.  
  2001.                     // Mark the old entry as deleted so it won't show up in searches.
  2002.                     // Will be left deleted unless they cancel the new entry.
  2003.                     (ohe->deleted) = TRUE;
  2004.  
  2005.                     if ((getPathString(data,dgs(MSG_PATHFORRENAME_FMT),TRUE,0,
  2006.                         nhe->path,HOTENTPATHLEN,nhe->name)) && (nhe->path[0]))
  2007.                     {
  2008.                         do
  2009.                         {
  2010.                             gsr=getString(data,dgs(MSG_NAMEFORRENAME_FMT),TRUE,0,
  2011.                                             nhe->name,HOTENTNAMELEN,nhe->path);
  2012.  
  2013.                             if ( (gsr) && (fher=findOldEntry(data,nhe->name)) )
  2014.                             {
  2015.                                 informUser(data,dgs(MSG_NAMEDUPLIC_FMT),TRUE,0,
  2016.                                                                         nhe->name);
  2017.                             }
  2018.                         } while( (gsr) && (fher) );
  2019.  
  2020.                         if ( (gsr) && (nhe->name[0]) )
  2021.                         {
  2022.                             setHotType(data,nhe);        // Set the type of entry.
  2023.                             needsave = TRUE;
  2024.                         }
  2025.                         else
  2026.                         {
  2027.                             // Else abort adding this entry.
  2028.                             remHotEntry(data);
  2029.                             (ohe->deleted) = FALSE;    // Undelete old entry.
  2030.                             break;                    // Don't do any more entries.
  2031.                         }
  2032.                     }
  2033.                     else
  2034.                     {
  2035.                         // Else abort adding this entry.
  2036.                         remHotEntry(data);
  2037.                         (ohe->deleted) = FALSE;    // Undelete old entry.
  2038.                         break;                    // Don't do any more entries either.
  2039.                     }
  2040.                 }
  2041.             }
  2042.         }
  2043.  
  2044.         if (needsave)
  2045.         {
  2046.             writeConfigFile(data);
  2047.             rereadConfig(data,combuf,cpp);
  2048.         }
  2049.  
  2050.         deleteResNode(&data->rnd,orn);
  2051.     }
  2052.  
  2053.     ReleaseSemaphore(configsemaphore);
  2054.  
  2055.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  2056.     sendExtCmd_nr(data,combuf,cpp);
  2057.  
  2058.     return(FALSE);
  2059. }
  2060.  
  2061. /*= event_parent() ===================================================================-.
  2062. `-====================================================================================*/
  2063. BOOL event_parent(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  2064.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  2065. {
  2066.     BOOL stoploop = FALSE;
  2067.     ConfigListNode *clnp;
  2068.  
  2069.     if (data->conflist)                    // 'Parent' hotlist-file, if there is one.
  2070.     {
  2071.         if (strstr(arg6,"shift"))
  2072.         {
  2073.             // If shift helf down, open a new hotlist for it.
  2074.  
  2075.             sprintf(combuf,"command hotlist NEW CONFIG \"%s\"",\
  2076.                     ((data->conflist->parent) ? (data->conflist->parent->rn->rn_Mem) :\
  2077.                                                 (data->originalconfig)) );
  2078.             sendExtCmd_nr(data,combuf,cpp);
  2079.         }
  2080.         else
  2081.         {
  2082.             // If shift not held down, read it into the current lister.
  2083.  
  2084.             // Get pointer to 'parent' config file's ConfigListNode.
  2085.             clnp = (data->conflist->parent);
  2086.  
  2087.             // Turn notify off before the filename mem is deallocated and made invalid.
  2088.             notifyOff(data);
  2089.  
  2090.             // Free the old, unwanted filename memory.
  2091.             deleteResNode(&data->rnd,data->conflist->rn);
  2092.             // Free the old, unwanted ConfigListNode.
  2093.             deleteResNode(&data->rnd,data->conflist->rn_self);
  2094.  
  2095.             // Make the parent of the 'old' node the top-level node.
  2096.             (data->conflist) = clnp;
  2097.  
  2098.             // Setup the filenames.
  2099.             if (data->conflist)
  2100.             {
  2101.                 // If there is still a parent config-file to go to, use it.
  2102.                 (data->config) = (data->conflist->rn->rn_Mem);
  2103.                 (data->notify.nr_Name) = (data->config);
  2104.             }
  2105.             else
  2106.             {
  2107.                 // If there's no parent config-file, use the original one.
  2108.                 (data->config) = (data->originalconfig);
  2109.             }
  2110.  
  2111.             // Update the title.
  2112.             sprintf(combuf,"lister set %s header (%s)",data->lister,data->config);
  2113.             sendExtCmd_nr(data,combuf,cpp);
  2114.             // Read in the 'new' config file.
  2115.             rereadConfig(data,combuf,cpp);
  2116.         }
  2117.     }
  2118.     else if (data->parent[0])            // Or 'real' parent dir, if there is one.
  2119.     {
  2120.         if (strstr(arg6,"shift"))
  2121.         {
  2122.             sprintf(combuf,"lister new %s",data->parent);
  2123.             sendExtCmd_nr(data,combuf,cpp);
  2124.         }
  2125.         else
  2126.         {
  2127.             stoploop = TRUE;
  2128.             listerRead(data,combuf,cpp,data->parent);
  2129.         }
  2130.     }
  2131.     else                                // Otherwise just beep.
  2132.         DisplayBeep(NULL);
  2133.  
  2134.     return(stoploop);
  2135. }
  2136.  
  2137. /*= event_duplicate() ================================================================-.
  2138. `-====================================================================================*/
  2139. BOOL event_duplicate(Hotlist_Data *data,char *combuf,struct command_packet *cpp,\
  2140.         char *arg1,char *arg2,char *arg3,char *arg4,char *arg5,char *arg6)
  2141. {
  2142.     BOOL needsave = FALSE;
  2143.     BOOL gsr, fher;
  2144.     HotEntry *ohe;
  2145.     HotEntry *nhe;
  2146.     ResNode *orn;
  2147.  
  2148.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"on");
  2149.     sendExtCmd_nr(data,combuf,cpp);
  2150.  
  2151.     getWriteConfigSemaphore(data);
  2152.  
  2153.     if (orn = allocNewResNode(&data->rnd,HOTENTNAMELEN))
  2154.     {
  2155.         // Point past the initial quote.
  2156.         if (*arg2 == '"') arg2++;
  2157.  
  2158.         while(*arg2)    // Do all words in the list (multi-drop).
  2159.         {
  2160.             wordcpy(orn->rn_Mem,&arg2);
  2161.  
  2162.             if (ohe = findHotEntry(data,orn->rn_Mem))
  2163.             {
  2164.                 if (nhe = newHotEntry(data))
  2165.                 {
  2166.                     strcpy(nhe->path,ohe->path);
  2167.                     strcpy(nhe->name,ohe->name);
  2168.  
  2169.                     if ((getPathString(data,dgs(MSG_PATHFORNEW),TRUE,0,
  2170.                         nhe->path,HOTENTPATHLEN)) && (nhe->path[0]))
  2171.                     {
  2172.                         do
  2173.                         {
  2174.                             gsr=getString(data,dgs(MSG_NAMEFORPATH_FMT),TRUE,0,
  2175.                                             nhe->name,HOTENTNAMELEN,nhe->path);
  2176.  
  2177.                             if ( (gsr) && (fher=findOldEntry(data,nhe->name)) )
  2178.                             {
  2179.                                 informUser(data,dgs(MSG_NAMEDUPLIC_FMT),TRUE,0,
  2180.                                                                         nhe->name);
  2181.                             }
  2182.                         } while( (gsr) && (fher) );
  2183.  
  2184.                         if ( (gsr) && (nhe->name[0]) )
  2185.                         {
  2186.                             setHotType(data,nhe);        // Set the type of entry.
  2187.                             needsave = TRUE;
  2188.                         }
  2189.                         else
  2190.                         {
  2191.                             // Else abort adding this entry.
  2192.                             remHotEntry(data);
  2193.                             break;                    // Don't do any more entries.
  2194.                         }
  2195.                     }
  2196.                     else
  2197.                     {
  2198.                         // Else abort adding this entry.
  2199.                         remHotEntry(data);
  2200.                         break;                    // Don't do any more entries either.
  2201.                     }
  2202.                 }
  2203.             }
  2204.         }
  2205.  
  2206.         if (needsave)
  2207.         {
  2208.             writeConfigFile(data);
  2209.             rereadConfig(data,combuf,cpp);
  2210.         }
  2211.  
  2212.         deleteResNode(&data->rnd,orn);
  2213.     }
  2214.  
  2215.     ReleaseSemaphore(configsemaphore);
  2216.  
  2217.     sprintf(combuf,"lister set %s busy %s wait",data->lister,"off");
  2218.     sendExtCmd_nr(data,combuf,cpp);
  2219.  
  2220.     return(FALSE);
  2221. }
  2222.  
  2223. /*= getConfigSemaphore() =============================================================-.
  2224. || Get a lock on the configfile semaphore, showing a progress window if it isn't      ||
  2225. || immediately obtainable.  obtype should be GCS_SHARED or GCS_EXCLUSIVE.              ||
  2226. || The semaphore returned should be freed with ReleaseSemaphore as usual.              ||
  2227. `-====================================================================================*/
  2228. void getConfigSemaphore(Hotlist_Data *data,short obtype)
  2229. {
  2230.     APTR progwin;
  2231.     struct Window *win;
  2232.     struct Screen *screen;
  2233.     LONG goddit;
  2234.  
  2235.     if (win = getListerWindow(data))
  2236.         screen = NULL;
  2237.     else
  2238.         screen = getDOpusScreen(data);
  2239.  
  2240.     if (obtype == GCS_SHARED)
  2241.         goddit = AttemptSemaphoreShared(configsemaphore);
  2242.     else
  2243.         goddit = AttemptSemaphore(configsemaphore);
  2244.  
  2245.     if ( !(goddit) )
  2246.     {
  2247.         progwin = OpenProgressWindowTags(
  2248.             TAGIF(win,PW_Window),    win,
  2249.             TAGIF((!win) && screen,PW_Screen),    screen,
  2250.             PW_Title,                dgs(MSG_TITLE),
  2251.             PW_FileName,            dgs(MSG_SEMWAIT),
  2252.             PW_Flags,                PWF_FILENAME,
  2253.             TAG_END);
  2254.  
  2255.         if (obtype == GCS_SHARED)
  2256.             ObtainSemaphoreShared(configsemaphore);
  2257.         else
  2258.             ObtainSemaphore(configsemaphore);
  2259.  
  2260.         if (progwin)
  2261.             CloseProgressWindow(progwin);
  2262.     }
  2263. }
  2264.  
  2265. /*= getWriteConfigSemaphore() ========================================================-.
  2266. || This routine first gets exclusive access to the config file semaphore and then      ||
  2267. || re-reads the config file if file notification reports that it has changed.          ||
  2268. || You should call this routine to get your semaphore lock prior to changing any      ||
  2269. || config file data and writting a new config file.                                      ||
  2270. || I repeat: PRIOR TO CHANGING ANY CONFIG FILE DATA -- Don't just call it right          ||
  2271. || before calling writeConfigFile() as if the config file has changed it'll be          ||
  2272. || re-read, replacing any changes you have just made, and then written out again      ||
  2273. || exactly the same.                                                                  ||
  2274. || You should release the semaphore lock after you're finished writting with a call      ||
  2275. || to ReleaseSemaphore() -- writeConfigFile() won't do this for you!                  ||
  2276. ||------------------------------------------------------------------------------------||
  2277. || Currently it doesn't re-read the changed file as the notification routines are yet ||
  2278. || to be written. I haven't even decided exactly how they will work.                  ||
  2279. `-====================================================================================*/
  2280. void getWriteConfigSemaphore(Hotlist_Data *data)
  2281. {
  2282.     // First, get us exclusive access to the config file.
  2283.     getConfigSemaphore(data,GCS_EXCLUSIVE);
  2284.  
  2285.     // Now, if the file has changed, re-read.
  2286.     if ((SetSignal(0,0)) & (data->notsigmask))
  2287.     {
  2288.         SetSignal(0,data->notsigmask);
  2289.         // readConfigFile() won't get it's own semaphore lock as 2nd argument TRUE.
  2290.         readConfigFile(data,TRUE);
  2291.     }
  2292. }
  2293.  
  2294. /*= notifyOn() =======================================================================-.
  2295. || Routine to handle turning on notification on the config file. Will set              ||
  2296. || data->notifyon to TRUE if it succeeds. Will not start another notification          ||
  2297. || request if data->notifyon indicates we already have one. As some filesystems do      ||
  2298. || not support notification, notification may still be off after this call.              ||
  2299. || The signal bit used for notification will be cleared.                              ||
  2300. `-====================================================================================*/
  2301. void notifyOn(Hotlist_Data *data)
  2302. {
  2303.     // Unless notification already on, attempt to turn it on.
  2304.     if (!(data->notifyon))
  2305.     {
  2306.         // If the signal is set, clear it.
  2307.         // We could just clear it, but the autodocs/RKRM discourage it.
  2308.         if ((SetSignal(0,0)) & (data->notsigmask))
  2309.             SetSignal(0,data->notsigmask);
  2310.  
  2311.         (data->notifyon) = StartNotify(&data->notify);
  2312.     }
  2313. }
  2314.  
  2315. /*= notifyOff() ======================================================================-.
  2316. || If data->notifyon indicates we have a notification request this routine will turn  ||
  2317. || it off and set data->notifyon to FALSE.                                              ||
  2318. `-====================================================================================*/
  2319. void notifyOff(Hotlist_Data *data)
  2320. {
  2321.     if (data->notifyon)
  2322.     {
  2323.         EndNotify(&data->notify);
  2324.         (data->notifyon) = FALSE;
  2325.     }
  2326. }
  2327.  
  2328. /*= SkipSpaces() =====================================================================-.
  2329. || Returns pointer to just after the nth space in the string.                          ||
  2330. || Returns pointer to the NULL at the end of the string if not enough spaces.          ||
  2331. `-====================================================================================*/
  2332. char *skipSpaces(char *text,int numspac)
  2333. {
  2334.     if (text)
  2335.     {
  2336.         while (*text && numspac)
  2337.         {
  2338.             if (*(text++) == ' ')
  2339.                 numspac--;
  2340.         }
  2341.     }
  2342.     return text;
  2343. }
  2344.